import numpy as np


# parse csi-data file
class Bfee:

    def __init__(self):
        pass

    @staticmethod
    def FromFile(fileName, model_name_encode="shift-JIS"):

        with open(fileName, "rb") as f:
            from functools import reduce
            # reduce(function，list)，summary of list by order
            array = bytes(reduce(lambda x, y: x + y, list(f)))

        bfee = Bfee()

        # vmd.current_index = 0
        bfee.fileLength = len(array)
        bfee.dicts = []
        bfee.allCsi = []
        '''
        Initialize variables
        ret = cell(ceil(len/95),1);
        Holds the return values-1x1 CSI is 95 bytes big, so this should be upper bound
        '''
        currentOffIntoFile = 0  # % Current offset into file
        count = 0  # % Number of records output
        # brokenPerm = 0  # % Flag marking whether we've encountered a broken CSI yet
        triangle = [0, 1, 3]  # % What perm should sum to for 1,2,3 antennas

        fileLengthOffset=bfee.fileLength - 3
        while currentOffIntoFile < fileLengthOffset:
            '''
            read date file to array which is sizeA ，
            make file pointer to the last read value
            fread fill A with order of column
            '''
            currentOffIndex = currentOffIntoFile+2
            arrayTemp=array[currentOffIntoFile:currentOffIndex]
            bfee.fieldLength=int.from_bytes(arrayTemp,byteorder='big', signed=False)
            bfee.code = array[currentOffIndex]
            currentOffIntoFile = currentOffIntoFile + 3
            '''
            there is CSI in field if code == 187，
            If unhandled code skip (seek over) the record and continue
            '''
            if bfee.code == 187:
                pass
            else:
                # % skip all other info
                currentOffIntoFile = currentOffIntoFile + bfee.fieldLength - 1
                continue

            # get beamforming or phy data
            if bfee.code == 187:
                count = count + 1
                currentOffIndex = currentOffIntoFile+4
                arrayTemp = array[currentOffIntoFile:currentOffIndex]
                bfee.timestampLow = int.from_bytes(arrayTemp, byteorder='little', signed=False)

                currentOffIndex2 = currentOffIntoFile + 6
                arrayTemp2 = array[currentOffIndex:currentOffIndex2]
                bfee.bfeeCount = int.from_bytes(arrayTemp2, byteorder='little', signed=False)
                bfee.nrx = array[currentOffIntoFile + 8]
                bfee.ntx = array[currentOffIntoFile + 9]
                bfee.rssiA = array[currentOffIntoFile + 10]
                bfee.rssiB = array[currentOffIntoFile + 11]
                bfee.rssiC = array[currentOffIntoFile + 12]
                bfee.noise = array[currentOffIntoFile + 13] - 256
                bfee.agc = array[currentOffIntoFile + 14]
                bfee.antennaSel = array[currentOffIntoFile + 15]

                currentOffIndex3 = currentOffIntoFile + 16
                currentOffIndex4 = currentOffIntoFile + 18
                currentOffIndex5 = currentOffIntoFile + 20
                arrayTemp3 = array[currentOffIndex3:currentOffIndex4]
                arrayTemp4 = array[currentOffIndex4:currentOffIndex5]
                bfee.length=int.from_bytes(arrayTemp3,byteorder='little', signed=False)
                bfee.fakeRateNoFlags=int.from_bytes(arrayTemp4,
                                                    byteorder='little', signed=False)
                temp = bfee.nrx * bfee.ntx * 8 * 2 + 3
                bfee.calcLength = (30 * temp + 6) / 8
                shape = 30, bfee.nrx, bfee.ntx
                dtype = np.dtype(np.complex)
                bfee.csi = np.zeros(shape,dtype)
                bfee.perm = [1, 2, 3]
                bfee.perm[0] = bfee.antennaSel & 0x3
                csiTemp = bfee.antennaSel >> 2
                bfee.perm[1] = csiTemp & 0x3
                csiTemp = bfee.antennaSel >> 4
                bfee.perm[2] = csiTemp & 0x3

                currentOffIntoFile = currentOffIntoFile + 20

                # get payload
                currentOffset=currentOffIntoFile + bfee.length
                payload = array[currentOffIntoFile:currentOffset]
                currentOffIntoFile = currentOffIntoFile + bfee.length

                index = 0

                # Check that length matches what it should
                if bfee.length != bfee.calcLength:
                    print("MIMOToolbox:read_bfee_new:size", "Wrong beamforming matrix size.")

                # Compute CSI from all this crap :
                # import struct
                for iTemp in range(30):
                    index += 3
                    remainder = index % 8
                    for jTemp in range(bfee.nrx):
                        for kTemp in range(bfee.ntx):
                            csiTemp = index / 8
                            valueTemp = int(csiTemp)
                            index2=payload[valueTemp]
                            index2= index2 >> remainder
                            csiTemp = index / 8 + 1
                            valueTemp = int(csiTemp)
                            index3=payload[valueTemp]
                            csiTemp = 8 - remainder
                            index3 = index3 << csiTemp
                            index4=index2 | index3
                            realBin = bytes([index4 & 0b11111111])
                            real = int.from_bytes(realBin, byteorder='little', signed=True)
                            csiTemp = index / 8 + 1
                            valueTemp = int(csiTemp)
                            index5 = payload[valueTemp]
                            index5 = index5 >> remainder
                            csiTemp = index / 8 + 2
                            valueTemp = int(csiTemp)
                            index6 = payload[valueTemp]
                            csiTemp = 8 - remainder
                            index6 = index6 << csiTemp
                            index7 = index5 | index6
                            imagBin = bytes([index7 & 0b11111111])
                            imag = int.from_bytes(imagBin, byteorder='little', signed=True)

                            tmp = np.complex(float(real), float(imag))
                            bfee.csi[iTemp, jTemp, kTemp] = tmp
                            index += 16

                # matrix does not contain default values
                if sum(bfee.perm) != triangle[bfee.nrx - 1]:
                    print('WARN ONCE: Found CSI (', fileName, ') with Nrx=',
                          bfee.nrx, ' and invalid perm=[', bfee.perm, ']\n')
                else:
                    dtype = np.dtype(np.complex)
                    tempCsi = np.zeros(bfee.csi.shape,dtype)

                    # bfee.csi[:,bfee.perm[0:bfee.Nrx],:] = bfee.csi[:,0:bfee.Nrx,:]
                    for r in range(bfee.nrx):
                        tempCsi[:, bfee.perm[r], :] = bfee.csi[:, r, :]
                    bfee.csi = tempCsi

                # export attributes of class to dict，then return
                bfeeDict = {'timestamp_low': bfee.timestampLow,
                            'bfee_count': bfee.bfeeCount,
                            'Nrx': bfee.nrx,
                            'Ntx': bfee.ntx,
                            'rssi_a': bfee.rssiA,
                            'rssi_b': bfee.rssiB,
                            'rssi_c': bfee.rssiC,
                            'noise': bfee.noise,
                            'agc': bfee.agc,
                            'antenna_sel': bfee.antennaSel,
                            'perm': bfee.perm,
                            'len': bfee.length,
                            'fake_rate_n_flags': bfee.fakeRateNoFlags,
                            'calc_len': bfee.calcLength,
                            'csi': bfee.csi}

                bfee.dicts.append(bfeeDict)
                bfee.allCsi.append(bfee.csi)

        return bfee
